package com.nvwa.coding.util.safe;

import com.nvwa.coding.exception.WrappedException;
import com.nvwa.coding.function.SupplierEX;
import com.nvwa.coding.model.ValueValidator;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import static com.nvwa.coding.util.safe.Transformer.boolOpt;

public abstract class Checker extends Safer {

    public static <T> boolean allMatch(Predicate<T> test, Iterable<T> iterable) {
        if (test == null || iterable == null) {
            throw new IllegalArgumentException();
        }
        for (T one : iterable) {
            if (!test.test(one)) {
                return false;
            }
        }
        return true;
    }

    @SafeVarargs
    public static <T> boolean allMatch(Predicate<T> test, T... array) {
        return allMatch(test, Arrays.stream(array)::iterator);
    }

    public static <T extends Iterable<E>, E> Predicate<T> allMatch(Predicate<E> test) {
        return collection -> allMatch(test, collection);
    }

    @SafeVarargs
    public static <T, F> Predicate<T> allFieldMatch(Predicate<F> test, Function<T, F>... mappers) {
        return t -> allMatch(test, stream(mappers).map(mapper -> mapper.apply(t))::iterator);
    }

    public static <T> boolean anyMatch(Predicate<T> test, Iterable<T> iterable) {
        if (test == null || iterable == null) {
            throw new IllegalArgumentException();
        }
        for (T one : iterable) {
            if (test.test(one)) {
                return true;
            }
        }
        return false;
    }

    @SafeVarargs
    public static <T> boolean anyMatch(Predicate<T> test, T... array) {
        return anyMatch(test, Arrays.stream(array)::iterator);
    }

    public static <T extends Iterable<E>, E> Predicate<T> anyMatch(Predicate<E> test) {
        return collection -> anyMatch(test, collection);
    }

    @SafeVarargs
    public static <T, F> Predicate<T> anyFieldMatch(Predicate<F> test, Function<T, F>... mappers) {
        return t -> anyMatch(test, stream(mappers).map(mapper -> mapper.apply(t))::iterator);
    }

    public static <T> boolean nonMatch(Predicate<T> test, Iterable<T> iterable) {
        return !anyMatch(test, iterable);
    }

    @SafeVarargs
    public static <T> boolean nonMatch(Predicate<T> test, T... array) {
        return nonMatch(test, Arrays.stream(array)::iterator);
    }

    public static <T extends Iterable<E>, E> Predicate<T> nonMatch(Predicate<E> test) {
        return collection -> nonMatch(test, collection);
    }

    @SafeVarargs
    public static <T, F> Predicate<T> nonFieldMatch(Predicate<F> test, Function<T, F>... mappers) {
        return t -> nonMatch(test, stream(mappers).map(mapper -> mapper.apply(t))::iterator);
    }


    public static boolean isNull(Object o) {
        return o == null;
    }

    public static boolean notNull(Object o) {
        return o != null;
    }

    public static boolean isNull(SupplierEX<Object, ?> supplier) {
        return supplier == null || isNull(silentGetOrNull(supplier));
    }

    public static boolean notNull(SupplierEX<Object, ?> supplier) {
        return !isNull(supplier);
    }

    public static <T> boolean notNullAnd(T o, Predicate<T> filter) {
        return o != null && filter.test(o);
    }

    public static <T> Predicate<T> notNullAnd(Predicate<T> filter) {
        return o -> notNullAnd(o, filter);
    }

    public static boolean isTrue(Object o) {
        return boolOpt(o).orElse(false);
    }

    public static boolean notTrue(Object o) {
        return !isTrue(o);
    }

    public static boolean isTrue(SupplierEX<Object, ?> supplier) {
        return supplier == null || isTrue(silentGetOrNull(supplier));
    }

    public static boolean notTrue(SupplierEX<Object, ?> supplier) {
        return !isTrue(supplier);
    }

    public static boolean isFalse(Object o) {
        return !boolOpt(o).orElse(true);
    }

    public static boolean notFalse(Object o) {
        return !isFalse(o);
    }

    public static boolean isFalse(SupplierEX<Object, ?> supplier) {
        return supplier == null || isFalse(silentGetOrNull(supplier));
    }

    public static boolean notFalse(SupplierEX<Object, ?> supplier) {
        return !isFalse(supplier);
    }


    public static boolean isBlank(String s) {
        return length(s) == 0 || length(s.trim()) == 0 || "null".equalsIgnoreCase(s) || "undefined".equalsIgnoreCase(s);
    }

    public static boolean notBlank(String s) {
        return !isBlank(s);
    }

    public static boolean isBlank(SupplierEX<String, ?> supplier) {
        return supplier == null || isBlank(silentGetOrNull(supplier));
    }

    public static boolean notBlank(SupplierEX<String, ?> supplier) {
        return !isBlank(supplier);
    }

    public static <E> boolean isEmpty(E[] array) {
        return length(array) == 0;
    }

    public static <E> boolean notEmpty(E[] array) {
        return !isEmpty(array);
    }

    public static <E> boolean isEmpty(Collection<E> collection) {
        return size(collection) == 0;
    }

    public static <E> boolean notEmpty(Collection<E> collection) {
        return !isEmpty(collection);
    }

    public static boolean isEmpty(SupplierEX<Collection<?>, ?> supplier) {
        return supplier == null || isEmpty(silentGetOrNull(supplier));
    }

    public static boolean notEmpty(SupplierEX<Collection<?>, ?> supplier) {
        return !isEmpty(supplier);
    }

    public static <K, V> boolean isEmpty(Map<K, V> map) {
        return size(map) == 0;
    }

    public static <K, V> boolean notEmpty(Map<K, V> map) {
        return !isEmpty(map);
    }

    public static boolean isEmptyMap(SupplierEX<Map<?, ?>, ?> supplier) {
        return supplier == null || isEmpty(silentGetOrNull(supplier));
    }

    public static boolean notEmptyMap(SupplierEX<Map<?, ?>, ?> supplier) {
        return !isEmptyMap(supplier);
    }

    /**
     * 是否有小数点部分
     */
    public static boolean isDecimalNumber(Number number) {
        if (isNull(number)) {
            return false;
        }
        if (number instanceof Integer || number instanceof Long || number instanceof Short || number instanceof Byte || number instanceof BigInteger) {
            return false;
        }
        return Math.abs(number.doubleValue() % 1) > 0;
    }

    public static boolean isEqualsZero(Number number) {
        return number != null && number.doubleValue() == 0.0D;
    }

    public static boolean isMoreThanZero(Number number) {
        return number != null && number.doubleValue() > 0;
    }

    public static boolean notMoreThanZero(Number number) {
        return !isMoreThanZero(number);
    }

    public static boolean isLessThanZero(Number number) {
        return number != null && number.doubleValue() < 0;
    }

    public static boolean notLessThanZero(Number number) {
        return !isLessThanZero(number);
    }

    public static boolean isMoreEqualsZero(Number number) {
        return number != null && number.doubleValue() >= 0;
    }

    public static boolean notMoreEqualsZero(Number number) {
        return !isMoreEqualsZero(number);
    }

    public static boolean isLessEqualsZero(Number number) {
        return number != null && number.doubleValue() <= 0;
    }

    public static boolean notLessEqualsZero(Number number) {
        return !isLessEqualsZero(number);
    }

    public static boolean isValueValid(Object o) {
        if (o == null) {
            return false;
        }
        if (o instanceof ValueValidator) {
            return ((ValueValidator) o).valid();
        }
        if (o instanceof Boolean) {
            return (boolean) o;
        }
        if (o instanceof String) {
            return !((String) o).isEmpty();
        }
        if (o instanceof Number) {
            return ((Number) o).doubleValue() > 0;
        }
        if (o instanceof Collection) {
            return !((Collection<?>) o).isEmpty();
        }
        if (o instanceof Map) {
            return !((Map<?, ?>) o).isEmpty();
        }
        return true;
    }

    public static boolean notValueValid(Object o) {
        return !isValueValid(o);
    }


    public static <T> T withCheck(T t, Predicate<T> checker, Supplier<? extends Throwable> throwSupplier) {
        if (checker.test(t)) {
            return t;
        }
        WrappedException.wrapThrow(throwSupplier.get());
        return t;
    }

    public static <T> T withCheck(T t, Predicate<T> checker) {
        return withCheck(t, checker, () -> new IllegalStateException(String.valueOf(t)));
    }

    public static <T> T withCheck(T t) {
        return withCheck(t, Objects::nonNull, () -> new NullPointerException());
    }


}
